package MgoPool

import (
	"errors"
	"fmt"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"gopkg.in/mgo.v2/txn"
	"math"
	"reflect"
	"regexp"
	"strings"
)

var default_TRANS_TABLE = "transaction"

/**
filed需继承
*/
type BaseFieldInterface interface {
	CollectionName() string
}

type BaseMgoManyModel struct {
	builderName string
}

func (this *BaseMgoManyModel) GetSession(s func(session *mgo.Session, db *mgo.Database) error) error {
	return MPool.GetSession(this.builderName, s)
}

/**
只获取session但不设置回调
[注]
获取后需手动释放session,  session.Close()
*/
func (this *BaseMgoManyModel) GetSessionOnly() (*mgo.Session, *mgo.Database, error) {
	return MPool.GetSessionOnly(this.builderName)
}

func (this *BaseMgoManyModel) SetBuildName(builderName string) {
	this.builderName = builderName
}

func (this *BaseMgoManyModel) GetBuilder() (MgoBuilderApi, error) {
	return MPool.GetBuilder(this.builderName)
}

/**
集后分片 - hash
@param collectionName string 集合名称
@param shareKey string 分键键
@param sessionParam *mgo.Session 如果为空则获取新的session
*/
func (this *BaseMgoManyModel) ShardCollectionHashed(collectionName string, shareKey string, sessionParam *mgo.Session) error {
	tb, err := this.GetBuilder()
	if err != nil {
		return err
	}
	dataBase := tb.GetDataBase()
	command := bson.D{{"shardCollection", fmt.Sprintf("%s.%s", dataBase, collectionName)}, {"key", bson.D{{shareKey, "hashed"}}}}
	if sessionParam == nil {
		session, _, err := this.GetSessionOnly()
		if err != nil {
			return err
		}
		defer session.Close()
		return session.DB("admin").Run(command, nil)

	} else {
		return sessionParam.DB("admin").Run(command, nil)
	}
}

/**
简版多文档提交
[主要是以mgo _id 为主键关联]
@param session *mgo.Session  mgo session
@param db *mgo.Database mgo db
@param opts []txn.Op 多文档操作条件
*/
func (this *BaseMgoManyModel) MultiTrans(session *mgo.Session, db *mgo.Database, ops []txn.Op) error {
	return this.MultiTransactions(session, db, ops, "", "", nil)
}

/**
多文档提交
@param session *mgo.Session  mgo session
@param db *mgo.Database mgo db
@param opts []txn.Op 多文档操作条件
@param transTable string 类似事务记录表
@param id bson.ObjectId 更新的主键id
@param info interface{}
*/
func (this *BaseMgoManyModel) MultiTransactions(session *mgo.Session, db *mgo.Database, ops []txn.Op, transTable string, id bson.ObjectId, info interface{}) error {
	tb := default_TRANS_TABLE
	if len(strings.TrimSpace(transTable)) > 0 {
		tb = transTable
	}
	tc := db.C(tb)
	runer := txn.NewRunner(tc)
	opt := ops
	err := runer.Run(opt, id, info)
	if err != nil {
		return err
	}
	return nil
}

/**
获取字段下的bson
@param structs BaseFieldInterface  字段结构体
@param key string 结构体属性字段
@param hasTable bool 返回时是否需字段前带表名
@param aliasTable string 别名
*/
func (b *BaseMgoManyModel) getFieldTag(structs BaseFieldInterface, key string, hasTable bool, aliasTable string) []string {
	object := reflect.ValueOf(structs)
	myref := object
	typeOfType := myref.Type()
	var dbField []string
	for i := 0; i < myref.NumField(); i++ {
		itemField := typeOfType.Field(i)
		if key != "" && itemField.Name == key {
			tagColumn, err := getColumn(itemField.Tag.Get("bson"))
			if err == nil {
				if hasTable {
					if aliasTable != "" {
						dbField = []string{aliasTable + tagColumn}
					} else {
						dbField = []string{structs.CollectionName() + tagColumn}
					}

				} else {
					dbField = []string{tagColumn}
				}
			}
			break
		} else {
			tagColumn, err := getColumn(itemField.Tag.Get("bson"))
			if err == nil {
				if tagColumn != "" && tagColumn != "-" {
					if hasTable {
						if aliasTable != "" {
							dbField = []string{aliasTable + tagColumn}
						} else {
							dbField = append(dbField, structs.CollectionName()+tagColumn)
						}

					} else {
						dbField = append(dbField, tagColumn)
					}
				}
			}
		}

	}
	return dbField
}

//查找bson column
func getColumn(Tag string) (string, error) {
	columnREG := `(.*);?`
	regx := regexp.MustCompile(columnREG)
	s := regx.FindAllStringSubmatch(Tag, -1)
	if len(s) == 0 {
		return "", errors.New(fmt.Sprintf("bson match error %s", columnREG))
	}
	column := s[0][1]
	return column, nil
}

/**
获取所有 tag
*/
func (b *BaseMgoManyModel) GetAllFieldTag(structs BaseFieldInterface) bson.M {
	tags := b.getFieldTag(structs, "", false, "")
	tagsArray := bson.M{}
	for _, v := range tags {
		tagsArray[v] = 1
	}
	return tagsArray
}

/**
获取指定tag
*/
func (b *BaseMgoManyModel) GetItemTag(structs BaseFieldInterface, key string) string {
	item := b.getFieldTag(structs, key, false, "")
	return item[0]
}

/**
获取指定标签 并转为别名
*/
func (b *BaseMgoManyModel) GetItemTagAliasTable(structs BaseFieldInterface, key string, AliasTable string) string {
	item := b.getFieldTag(structs, key, true, AliasTable)
	return item[0]
}

func (b *BaseMgoManyModel) GetItemTagNotTable(structs BaseFieldInterface, key string) string {
	item := b.getFieldTag(structs, key, false, "")
	return item[0]
}

/**
@title 总页数计算
@param page int 当前页
@param pageSize int 每页显示
@param totalNum int 总记录数
*/
func (b *BaseMgoManyModel) Paginator(page int, pageSize int, totalNum int) int {
	totalPages := int(math.Ceil(float64(totalNum) / float64(pageSize))) //page总数
	return totalPages
}
